home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / OTStreamLogViewer / IC Libraries / ICCommonSubs.p < prev    next >
Encoding:
Text File  |  2000-09-28  |  42.9 KB  |  1,402 lines  |  [TEXT/CWIE]

  1. unit ICCommonSubs;
  2.  
  3. (*    This file is part of the Internet Configuration system and is placed in the public domain for the benefit of all.
  4.  
  5.     This file holds all those miscellaneous little functions that are basically wrappers
  6.     around existing OS functionality.
  7. *)
  8.  
  9. interface
  10.  
  11.     uses
  12.         Files,
  13.         Windows,
  14.         Lists, 
  15.         AppleEvents,
  16.         Menus,
  17.  
  18.         InternetConfig;
  19.  
  20.     (* ***** Event Manager Stuff ***** *)
  21.  
  22.     (* A collection of useful ASCII character definitions. *)            
  23.     const
  24.         kNulChar            = chr($00);
  25.         kHomeChar             = chr($01);
  26.         kEnterChar             = chr($03);
  27.         kEndChar             = chr($04);
  28.         kHelpChar             = chr($05);
  29.         kBackSpaceChar         = chr($08);
  30.         kTabChar             = chr($09);
  31.         kLineFeedChar         = chr($0A);
  32.         kPageUpChar         = chr($0B);
  33.         kPageDownChar         = chr($0C);
  34.         kCRChar             = chr($0D);
  35.         kEscChar             = chr($1B);
  36.         kClearChar             = chr($1B);
  37.         kLeftArrowChar         = chr($1C);
  38.         kRightArrowChar     = chr($1D);
  39.         kUpArrowChar         = chr($1E);
  40.         kDownArrowChar        = chr($1F);
  41.         kSpaceChar             = chr($20);
  42.         kDelChar             = chr($7F);
  43.         kBulletChar         = chr($A5);
  44.  
  45.     (* A collection of useful virtual key code definitions. *)            
  46.     const
  47.         kUndoKeyCode         = 122;
  48.         kCutKeyCode         = 120;
  49.         kCopyKeyCode         = 99;
  50.         kPasteKeyCode         = 118;
  51.         kClearKeyCode         = 71;
  52.  
  53.         kEscKeyCode         = 53;
  54.         kReturnKeyCode         = 36;
  55.         kEnterKeyCode         = 52;
  56.         kTabKeyCode         = 48;
  57.         kSpaceKeyCode        = 49;
  58.         KDeleteKeyCode         = 51;
  59.  
  60.         kCommandKeyCode     = 55;
  61.         kShiftKeyCode         = 56;
  62.         kCapsLockKeyCode     = 57;
  63.         kOptionKeyCode         = 58;
  64.  
  65.     (* ***** Memory Stuff ***** *)
  66.     
  67.     const
  68.         kHandleLockBit = 7;
  69.         kHandlePurgeBit = 6;
  70.         kHandleResourceBit = 5;
  71.         
  72.         kHandleLockMask = $80;
  73.         kHandlePurgeMask = $40;
  74.         kHandleResourceMask = $20;
  75.         
  76.     type
  77.         (* A data structure for addresses memory as bytes. *)
  78.         BigBuffer = packed array [0..$0FFFFFF] of Byte;
  79.         BigBufferPtr = ^BigBuffer;
  80.         BigBufferHandle = ^BigBufferPtr;
  81.  
  82.         (* Another for addressing memory as chars. *)
  83.         BigCharArray = packed array [0..$FFFFFF] of char;
  84.         BigCharArrayPtr = ^BigCharArray;
  85.         BigCharArrayHandle = ^BigCharArrayPtr;
  86.  
  87.     function BlockCompare (lhsBaseAddr : univ Ptr; rhsBaseAddr: univ Ptr; size: longint): Boolean;
  88.         (* Compares two blocks of memory for equality.*)
  89.         
  90.     procedure BlockFill (baseAddr: univ Ptr; size: longint; value: integer);
  91.         (* Fills a block of memory with the value.  The memory is filled
  92.             as bytes, ie the high byte of value is ignored.
  93.         *)
  94.  
  95.     (* ***** Resource Manager Stuff ***** *)
  96.  
  97.     function CheckMemError(memoryHandle : univ Handle) : ICError;
  98.     function CheckResError(resourceHandle : univ Handle) : ICError;
  99.  
  100.     function AddNamedResource(data : Handle; theType : ResType; const name : Str255) : ICError;
  101.         (*    Adds data to the current resource file as a resource
  102.             of type theType with the given name.  It calculates a
  103.             unique resource ID for the newly added resource.  Note that,
  104.             like AddResource, data comes back as either a resource
  105.             handle (on noErr) or still a memory handle (on error).
  106.         *)
  107.         
  108.     function Set1Resource(theData : Handle; theType : ResType; theID : integer) : OSStatus;
  109.         (*    This routine sets the resource denoted by theType and theID to contain
  110.             theData.  If the resource does not currently exist, it is created.
  111.             If it does currently exist, it is modified.  theData is not disposed
  112.             of.
  113.         *)
  114.     function Set1ResourcePtr(theData : univ Ptr; theDataSize : longint; theType : ResType; theID : integer) : OSStatus;
  115.         (*    Works like Set1Resource except that you pass in a pointer and size. *)
  116.     
  117.     function Set1NamedResource(theData : Handle; theType : ResType; const name : Str255) : OSStatus;
  118.         (*    This routine sets the resource denoted by theType and name to contain
  119.             theData.  If the resource does not currently exist, it is created with
  120.             a unique ID greater than 127.  If it does currently exist, it is modified.
  121.             theData is not disposed of.
  122.         *)
  123.  
  124.     (* ***** File Manager Stuff ***** *)
  125.     
  126.     function GetVolInfo (var ioName: Str63; var ioVRefNum: integer; ioVolIndex: integer;
  127.                                         var ioVCrDate: longint): OSStatus;
  128.         (* Returns information about the specified volume. Basically this is a wrapper
  129.             around PBGetVInfo.  See IM:Files for a description of the meaning of the
  130.             ioName, ioVRefNum and ioVolIndex parameters.  This routine also returns
  131.             the volume's creation date in ioVCrDate to aid in Poor Man's Alias Manager
  132.             volume matching.
  133.         *)
  134.         
  135.     function FindApplicationInDTDB (creator: OSType; var foundApplicationSpec: FSSpec): OSStatus;
  136.         (* This routine attempts to find an application in the desktop database
  137.             given its creator type.
  138.         *)
  139.         
  140.     function FSpGetCatInfo (var fss: FSSpec; ioFDirIndex: integer; var cpb: CInfoPBRec): OSStatus;
  141.         (* This routine is a simple wrapper around PBGetCatInfo.  See IM:Files
  142.             for a description of the meaning of ioFDirIndex.
  143.             Note that, despite the name, this routine can be called under System 6.
  144.         *)
  145.         
  146.     function FSpSetCatInfo (var fss: FSSpec; var cpb: CInfoPBRec): OSStatus;
  147.         (* This routine is a simple wrapper around PBSetCatInfo.
  148.             Note that, despite the name, this routine can be called under System 6.
  149.         *)
  150.         
  151.     function FSpCatMoveQ(var fss : FSSpec; destDirID : longint) : OSStatus;
  152.         (*    A nicer wrapper around PBCatMove.  FSpCatMove is a horrible
  153.             routine because it requires a dest FSSpec, rather than
  154.             a dest dirID.  CatMove is not good either because the
  155.             Pascal interfaces define it to take a var:Str255 rather
  156.             than a StringPtr, so you can't pass nil for ioNewName.
  157.             So instead, we write our own.
  158.         *)
  159.  
  160.     function FileLocked (const fss: FSSpec): Boolean;
  161.         (* This routine returns true if the specified file is locked. Note that
  162.             this provides no guarantee that you can write to the file; it merely
  163.             checks all the things it can to see if any of them disable writing.
  164.         *)
  165.         
  166.     function CopyFork (sourceForkRefnum, destForkRefnum: integer; bytesToCopy: longint): OSStatus;
  167.         (* This routine copies a file fork from sourceForkRefnum to destForkRefnum.
  168.             The files must be positioned at where you want to start copying (usually
  169.             at the beginning) and the routine copys bytesToCopy bytes from the source
  170.             to the destination.
  171.         *)
  172.  
  173.     function CopyForkToFork (var sourceFile: FSSpec; var destFile: FSSpec;
  174.                             sourceRsrc: Boolean; destRsrc: Boolean): OSErr;
  175.         (* This routine copies a fork from the sourceFile to the destFile.
  176.             The fork chosen in each case is determined by the sourceRsrc and
  177.             destRsrc switches.  If true, the resource fork is used, if false,
  178.             the data fork.
  179.         *)
  180.  
  181.     function CopyFile (const source : FSSpec; const dest: FSSpec): OSStatus;
  182.         (* Copies a file from source to dest.  Any file already existing at dest is
  183.             destroyed.  Also set the catalogue info for the dest file from the source file.
  184.         *)
  185.     
  186.     function GetFSSpecGivenFileRefNum(fileRefNum : integer; var fss : FSSpec) : OSStatus;
  187.         (* Consults the FCB of the open file to find out its FSSpec. *)
  188.  
  189.     function FileRefNumIsWriteable(fileRefNum : integer; var writeable : Boolean) : OSStatus;
  190.         (* Returns true if the FCB says that the file is writable. *)
  191.  
  192.     function EqualFSSpec(const fss1 : FSSpec; const fss2 : FSSpec) : Boolean;
  193.         (* Returns true if fss1 and fss2 denote the same file system object. *)
  194.     
  195.     function ICUFSSpecToFullPath (const fss: FSSpec; var path: Str255): OSErr;
  196.         (* Returns a full path for the given FSSpec.  Actually it's an approximation
  197.             (it doesn't handle paths longer than 256 characters) but, seeing as
  198.             IC only uses this path for display purposes, that's not a problem.
  199.         *)
  200.  
  201.     function ICFileSpecToFSSpec (fileSpec: ICFileSpecHandle; canInteract: Boolean; var fss: FSSpec): ICError;
  202.     function FSSpecToICFileSpec (var fss: FSSpec; fileSpec: ICFileSpecHandle): OSErr;
  203.         (* These routines convert ICFileSpecHandles to FSSpecs and vice versa.
  204.             An ICFileSpecHandle is basically an AliasHandle with some fields tacked
  205.             on the front to make it usable by System 6 clients.  Under System 7 these
  206.             routines are basically wrappers for standard Alias Manager routines.
  207.             Note that in poth of these routines, the caller is responsible for allocating
  208.             and deallocating the ICFileSpecHandle, and we just resize it if necessary.
  209.         *)
  210.  
  211.     function IsApplicationType(fdType : OSType) : Boolean;
  212.         (*    Returns true if fdType is common type for applications. *)
  213.  
  214.     (* ***** IC API Stuff ***** *)
  215.  
  216.     (* These are simple wrappers around the IC API for getting and setting PString preferences. *)
  217.  
  218.     function ICGetPrefStr (inst: ICInstance; key: Str255; var attr: ICAttr; var str: Str255): ICError;
  219.     function ICSetPrefStr (inst: ICInstance; key: Str255; attr: ICAttr; str: Str255): ICError;
  220.  
  221.     (* ***** Text Utilities Stuff ***** *)
  222.  
  223.     (* These are simple wrappers around the toolbox NumToString and StringToNum routines. *)
  224.  
  225.     function DecStr(aNumber : longint): Str255;
  226.     function DecVal(aString : Str255) : longint;
  227.  
  228.     (* These are simple routines to convert between strings and OSTypes. *)
  229.     
  230.     function StringToOSType (aString: Str255): OSType;
  231.     function OSTypeToString (anOSType: OSType): Str15;
  232.  
  233.     function TPCopy (sourceString: string; startIndex : integer; count: integer): string;
  234.         (* TPCopy provides a version of the Pascal built-in function Copy that
  235.             implements the Think Pascal semantics.  This is much more useful
  236.             than routine built-in to Metrowerks Pascal, which implements the
  237.             semantics of MPW Pascal.
  238.             The routine extracts count characters from the source string
  239.             starting at character position start.  If there aren't enough characters
  240.             in the string, it returns what there are.
  241.         *)
  242.         
  243.     function GetOwnerName: Str255;
  244.         (* This function returns the Owner name for the Macintosh
  245.             as defined in the Sharing Setup control panel.
  246.         *)
  247.         
  248.     function NewLookupError(NersID : integer; errNum : OSStatus) : Str255;
  249.     (* Return the string associated with errNum.  The 'Ners' resource
  250.         has a ResEdit template of...
  251.                 *****
  252.                 LSTB
  253.                     errNum
  254.                     DLNG
  255.                     errstr
  256.                     PSTR
  257.                 *****
  258.                 LSTE
  259.         There must be a terminating entry with error number of 0 that contains
  260.         the default error message.
  261.     *)
  262.  
  263.     procedure NewLookupErrorC(NersID : integer; errNum : OSStatus; var result : Str255);
  264.     (* A procedure version of the above, for access from lame C-like
  265.         languages.
  266.     *)
  267.  
  268.     (* ***** Truly Misc Stuff ***** *)
  269.  
  270.     function TrapAvailable (theTrap: integer): Boolean;
  271.         (* Returns true if theTrap is available on this machine.
  272.             This routine is implemented by the book, the book being Inside Macintosh.
  273.         *)
  274.  
  275.     {$ifc not GENERATINGCFM}
  276.  
  277.     procedure MakeDataExecutableAs68KCode(base : Ptr; size : longint);
  278.         (* Makes some data executable as 68K code.  This is distinct
  279.             from the system call MakeDataExecutable, which makes data
  280.             executable as PowerPC code.
  281.            This call makes sense even on PPC machines, for example
  282.               the old contents of the memory might be currently cached
  283.               in the DR (Dynamic Recompiling) translation cache.
  284.         *)
  285.     
  286.     {$endc}
  287.  
  288.     function ICUCanInteract: ICError;
  289.         (* Returns noErr if user interaction is possible.  Basically a wrapper
  290.             around AEInteractWithUser that makes it System 6 safe.
  291.         *)
  292.  
  293.     procedure SafeAppendMenu (menuH: MenuHandle; itemText: Str255);
  294.         (* The system AppendMenu interprets the new menu item in strange
  295.             ways, attempting to glean command key and other information from 
  296.             the text.  This is obviously bad for things like the items on the 
  297.             Archie menu.  This 'safe' version of the AppendMenu routine
  298.             sets the text window interpreting it.
  299.         *)
  300.  
  301. implementation
  302.  
  303.     uses
  304.         Icons, 
  305.         Errors, 
  306.         Resources, 
  307.         Dialogs, 
  308.         ToolUtils, 
  309.         Traps, 
  310.         LowMem,
  311.         GestaltEqu,
  312.         FSM,
  313.         Script,
  314.         TextUtils,
  315.  
  316.         InternetConfig,
  317.         ICDebug;
  318.  
  319.     (* ***** Memory Stuff ***** *)
  320.  
  321. {$PUSH}
  322. {$R-}
  323.     function BlockCompare (lhsBaseAddr : univ Ptr; rhsBaseAddr: univ Ptr; size: longint): Boolean;
  324.         (* See comment in interface part. *)
  325.     begin
  326.         BlockCompare := false;
  327.         while (size > 0) do begin
  328.             if lhsBaseAddr^ <> rhsBaseAddr^ then begin
  329.                 exit(BlockCompare);
  330.             end; (* if *)
  331.             inc(longint(lhsBaseAddr));
  332.             inc(longint(rhsBaseAddr));
  333.             size := size - 1;
  334.         end; (* while *)
  335.         BlockCompare := true;
  336.     end; (* BlockCompare *)
  337.  
  338.     procedure BlockFill (baseAddr: univ Ptr; size: longint; value: integer);
  339.         (* See comment in interface part. *)
  340.     begin
  341.         while (size > 0) do begin
  342.             baseAddr^ := value;
  343.             inc(longint(baseAddr));
  344.             size := size - 1;
  345.         end; (* while *)
  346.     end; (* BlockFill *)
  347. {$POP}
  348.  
  349.     (* ***** Resource Manager Stuff ***** *)
  350.  
  351.     function CheckMemError(memoryHandle : univ Handle) : ICError;
  352.         var
  353.             err : ICError;
  354.     begin
  355.         err := MemError;
  356.         if err = noErr then begin
  357.             ICAssert(memoryHandle <> nil);
  358.             if memoryHandle = nil then begin
  359.                 err := memFullErr;
  360.             end; (* if *)
  361.         end; (* if *)
  362.         CheckMemError := err;
  363.     end; (* CheckMemError *)
  364.     
  365.     function CheckResError(resourceHandle : univ Handle) : ICError;
  366.         var
  367.             err : ICError;
  368.     begin
  369.         err := ResError;
  370.         if err = noErr then begin
  371.             if resourceHandle = nil then begin
  372.                 err := resNotFound;
  373.             end; (* if *)
  374.         end; (* if *)
  375.         CheckResError := err;
  376.     end; (* CheckResError *)
  377.  
  378.     function AddNamedResource(data : Handle; theType : ResType; const name : Str255) : ICError;
  379.         var
  380.             err : ICError;
  381.             id : integer;
  382.     begin
  383.         repeat
  384.             id := Unique1ID(theType);
  385.         until id > 127;
  386.         AddResource(data, theType, id, name);
  387.         err := ResError;
  388.         AddNamedResource := err;
  389.     end; (* AddNamedResource *)
  390.  
  391.     function Set1Resource(theData : Handle; theType : ResType; theID : integer) : OSStatus;
  392.         (*    This routine sets the resource denoted by theType and theID to contain
  393.             theData.  If the resource does not currently exist, it is created.
  394.             If it does currently exist, it is modified.  theData is not disposed
  395.             of.
  396.         *)
  397.         var
  398.             err : OSStatus;
  399.             theDataSize : longint;
  400.             oldData : Handle;
  401.     begin
  402.         theDataSize := GetHandleSize(theData);
  403.         
  404.         // Get the current contents of the resource.
  405.         
  406.         oldData := Get1Resource(theType, theID);
  407.         err := CheckResError(oldData);
  408.         if err = resNotFound then begin
  409.         
  410.             // There is currently no resource, add one that meets our needs.
  411.             
  412.             oldData := NewHandle(theDataSize);
  413.             err := CheckMemError(oldData);
  414.             if err = noErr then begin
  415.                 BlockMoveData(theData^, oldData^, theDataSize);
  416.                 AddResource(oldData, theType, theID, '');
  417.                 err := ResError;
  418.                 if err <> noErr then begin
  419.                     
  420.                     // If AddResource failed, oldData is still a memory
  421.                     // handle (ie it won't be cleaned up by the Resource Manager
  422.                     // when the resource file is closed), so we need to clean it
  423.                     // up ourselves.
  424.                     
  425.                     DisposeHandle(oldData);
  426.                     ICAssert(MemError = noErr);
  427.                 end; (* if *)
  428.             end; (* if *)
  429.             
  430.         end else begin
  431.         
  432.             // There is current a resource, copy our data into it.
  433.             
  434.             SetHandleSize(oldData, theDataSize);
  435.             err := MemError;
  436.             if err = noErr then begin
  437.                 BlockMoveData(theData^, oldData^, theDataSize);
  438.                 ChangedResource(oldData);
  439.                 err := ResError;
  440.             end; (* if *)
  441.             
  442.         end; (* if *)
  443.         Set1Resource := err;
  444.     end; (* Set1Resource *)
  445.     
  446.     function Set1ResourcePtr(theData : univ Ptr; theDataSize : longint; theType : ResType; theID : integer) : OSStatus;
  447.         var
  448.             err : OSStatus;
  449.             tmpH : Handle;
  450.     begin
  451.         err := PtrToHand(theData, tmpH, theDataSize);
  452.         if err = noErr then begin
  453.             err := Set1Resource(tmpH, theType, theID);
  454.             DisposeHandle(tmpH);
  455.             ICAssert(MemError = noErr);
  456.         end; (* if *)
  457.         Set1ResourcePtr := err;
  458.     end; (* Set1ResourcePtr *)
  459.     
  460.     function Set1NamedResource(theData : Handle; theType : ResType; const name : Str255) : OSStatus;
  461.         (*    This routine sets the resource denoted by theType and name to contain
  462.             theData.  If the resource does not currently exist, it is created with
  463.             a unique ID greater than 127.  If it does currently exist, it is modified.
  464.             theData is not disposed of.
  465.         *)
  466.         var
  467.             err : OSStatus;
  468.             theDataSize : longint;
  469.             oldData : Handle;
  470.     begin
  471.         theDataSize := GetHandleSize(theData);
  472.         
  473.         // Get the current contents of the resource.
  474.         
  475.         oldData := Get1NamedResource(theType, name);
  476.         err := CheckResError(oldData);
  477.         if err = resNotFound then begin
  478.         
  479.             // There is currently no resource, add one that meets our needs.
  480.             
  481.             oldData := NewHandle(theDataSize);
  482.             err := CheckMemError(oldData);
  483.             if err = noErr then begin
  484.                 BlockMoveData(theData^, oldData^, theDataSize);
  485.                 err := AddNamedResource(oldData, theType, name);
  486.                 if err <> noErr then begin
  487.                     
  488.                     // If AddNamedResource failed, oldData is still a memory
  489.                     // handle (ie it won't be cleaned up by the Resource Manager
  490.                     // when the resource file is closed), so we need to clean it
  491.                     // up ourselves.
  492.                     
  493.                     DisposeHandle(oldData);
  494.                     ICAssert(MemError = noErr);
  495.                 end; (* if *)
  496.             end; (* if *)
  497.             
  498.         end else begin
  499.         
  500.             // There is current a resource, copy our data into it.
  501.             
  502.             SetHandleSize(oldData, theDataSize);
  503.             err := MemError;
  504.             if err = noErr then begin
  505.                 BlockMoveData(theData^, oldData^, theDataSize);
  506.                 ChangedResource(oldData);
  507.                 err := ResError;
  508.             end; (* if *)
  509.             
  510.         end; (* if *)
  511.         Set1NamedResource := err;
  512.     end; (* Set1NamedResource *)
  513.     
  514.     (* ***** File Manager Stuff ***** *)
  515.  
  516.     function GetVolInfo (var ioName: Str63; var ioVRefNum: integer; ioVolIndex: integer;
  517.                                         var ioVCrDate: longint): OSStatus;
  518.         (* See comment in interface part. *)
  519.         var
  520.             err: OSStatus;
  521.             pb: ParamBlockRec;
  522.     begin
  523.         (* If we're trying to look up a volume by name, make sure there's a colon
  524.             on the end of the name.
  525.         *)
  526.         if (ioName <> '') & (ioName[length(ioName)] <> ':') then begin
  527.             ioName := concat(ioName, ':');
  528.         end; (* if *)
  529.         pb.ioNamePtr := @ioName;
  530.         pb.ioVRefNum := ioVRefNum;
  531.         pb.ioVolIndex := ioVolIndex;
  532.         err := PBGetVInfoSync(@pb);
  533.         if err = noErr then begin
  534.             ioVRefNum := pb.ioVRefNum;
  535.             ioVCrDate := pb.ioVCrDate;
  536.         end; (* if *)
  537.         GetVolInfo := err;
  538.     end; (* GetVolInfo *)
  539.  
  540.     function FindApplicationInDTDB (creator: OSType; var foundApplicationSpec: FSSpec): OSStatus;
  541.         (* See comment in interface part. *)
  542.         var
  543.             err: OSStatus;
  544.             junkCreationDate: longint;
  545.             volumeIndex: integer;
  546.             pbdt: DTPBRec;
  547.             found: Boolean;
  548.     begin
  549.         found := false;
  550.         (* Repeat through each of the volumes in their enumeration order,
  551.             querying the DTDB on each volume.
  552.         *)
  553.         volumeIndex := 1;
  554.         repeat
  555.             foundApplicationSpec.vRefNum := 0;
  556.             foundApplicationSpec.name := '';
  557.             err := GetVolInfo(foundApplicationSpec.name, foundApplicationSpec.vRefNum, volumeIndex, junkCreationDate);
  558.  
  559.             (* On this volume, attempt to find the application.  First get the path
  560.                 for the DTDB.
  561.             *)
  562.             if err = noErr then begin
  563.                 foundApplicationSpec.name := '';
  564.                 pbdt.ioNamePtr := @foundApplicationSpec.name;
  565.                 pbdt.ioVRefNum := foundApplicationSpec.vRefNum;
  566.                 err := PBDTGetPath(@pbdt);
  567.                 
  568.                 (* We have the path for the DTDB, now lookup the application in it. *)
  569.                 if err = noErr then begin
  570.                     pbdt.ioIndex := 0;
  571.                     pbdt.ioFileCreator := creator;
  572.                     err := PBDTGetAPPLSync(@pbdt);
  573.                     if err = noErr then begin
  574.                         found := true;
  575.                     end; (* if *)
  576.                 end; (* if *)
  577.                 
  578.                 (* Ignore errors from the DTDB, so we continue on with the next volume. *)
  579.                 err := noErr;
  580.             end; (* if *)
  581.             volumeIndex := volumeIndex + 1;
  582.         until found or (err <> noErr);
  583.         
  584.         (* Clean up.  If we found the application, set the parID.  Otherwise return an
  585.             innocuous FSSpec.
  586.         *)
  587.         if found then begin
  588.             err := noErr;
  589.             foundApplicationSpec.parID := pbdt.ioAPPLParID;
  590.         end else begin
  591.             err := afpItemNotFound;
  592.             foundApplicationSpec.vRefNum := 0;
  593.             foundApplicationSpec.parID := 2;
  594.             foundApplicationSpec.name := '';
  595.         end; (* if *)
  596.         FindApplicationInDTDB := err;
  597.     end; (* FindApplicationInDTDB *)
  598.  
  599.     function FSpGetCatInfo (var fss: FSSpec; ioFDirIndex: integer; var cpb: CInfoPBRec): OSStatus;
  600.         (* See comment in interface part. *)
  601.     begin
  602.         cpb.ioVRefNum := fss.vRefNum;
  603.         cpb.ioDirID := fss.parID;
  604.         cpb.ioNamePtr := @fss.name;
  605.         cpb.ioFDirIndex := ioFDirIndex;
  606.         FSpGetCatInfo := PBGetCatInfoSync(@cpb);
  607.     end; (* FSpGetCatInfo *)
  608.  
  609.     function FSpSetCatInfo (var fss: FSSpec; var cpb: CInfoPBRec): OSStatus;
  610.         (* See comment in interface part. *)
  611.     begin
  612.         cpb.ioVRefNum := fss.vRefNum;
  613.         cpb.ioDirID := fss.parID;
  614.         cpb.ioNamePtr := @fss.name;
  615.         FSpSetCatInfo := PBSetCatInfoSync(@cpb);
  616.     end; (* FSpSetCatInfo *)
  617.  
  618.     function FSpCatMoveQ(var fss : FSSpec; destDirID : longint) : OSStatus;
  619.         (* See comment in interface part. *)
  620.         var
  621.             cmpb : CMovePBRec;
  622.     begin
  623.         cmpb.ioNamePtr := @fss.name;
  624.         cmpb.ioVRefNum := fss.vRefNum;
  625.         cmpb.ioDirID := fss.parID;
  626.         cmpb.ioNewName := nil;
  627.         cmpb.ioNewDirID := destDirID;
  628.         FSpCatMoveQ := PBCatMoveSync(@cmpb);
  629.     end; (* FSpCatMoveQ *)
  630.  
  631.     function IsVolumeWriteable (vRefNum: integer): OSStatus;
  632.         (* This routine returns noErr if the specified volume is writable,
  633.             or an appropriate error otherwise.
  634.         *)
  635.         var
  636.             err: OSStatus;
  637.             pb: HParamBlockRec;
  638.     begin
  639.         pb.ioVRefNum := vRefNum;
  640.         pb.ioNamePtr := nil;
  641.         pb.ioVolIndex := 0;
  642.         err := PBHGetVInfoSync(@pb);
  643.  
  644.         if err = noErr then begin
  645.             if band(pb.ioVAtrb, $0080) <> 0 then begin
  646.                 err := wPrErr;            (* volume locked by hardware *)
  647.             end else if band(pb.ioVAtrb, $8000) <> 0 then begin
  648.                 err := vLckdErr;            (* volume locked by software *)
  649.             end; (* if *)
  650.         end; (* if *)
  651.  
  652.         IsVolumeWriteable := err;
  653.     end; (* IsVolumeWriteable *)
  654.  
  655.     function IsFileWriteable (fss: FSSpec): OSStatus;
  656.         (* This routine returns noErr if the specified file is writeable,
  657.             or an appropriate error otherwise.
  658.         *)
  659.         var
  660.             err: OSStatus;
  661.             cpb: CInfoPBRec;
  662.     begin
  663.         err := FSpGetCatInfo(fss, 0, cpb);
  664.         if err = noErr then begin
  665.             if band(cpb.ioFlAttrib, $01) <> 0 then begin
  666.                 err := fLckdErr;
  667.             end; (* if *)
  668.         end; (* if *)
  669.         IsFileWriteable := err;
  670.     end; (* IsFileWriteable *)
  671.  
  672.     function HGetDirAccess (ioVRefNum: integer; ioDirID: longint; ioName: StringPtr;
  673.                                             var ownerID, groupID, accessRights: longint): OSStatus;
  674.         (* This routine returns the directory access privileges for the specified directory. *)
  675.         var
  676.             err: OSStatus;
  677.             pb: HParamBlockRec;
  678.     begin
  679.         pb.ioNamePtr := ioName;
  680.         pb.ioVRefNum := ioVRefNum;
  681.         pb.ioDirID := ioDirID;
  682.         err := PBHGetDirAccessSync(@pb);
  683.         ownerID := pb.ioACOwnerID;
  684.         groupID := pb.ioACGroupID;
  685.         accessRights := pb.ioACAccess;
  686.         HGetDirAccess := err;
  687.     end; (* HGetDirAccess *)
  688.  
  689.     function FileLocked (const fss: FSSpec): Boolean;
  690.         (* See comment in interface part. *)
  691.         var
  692.             locked: Boolean;
  693.             junk: longint;
  694.             access: longint;
  695.     begin
  696.         locked := (IsVolumeWriteable(fss.vRefNum) <> noErr);
  697.         if not locked then begin
  698.             locked := (IsFileWriteable(fss) <> noErr);
  699.         end; (* if *)
  700.         if not locked then begin
  701.             if HGetDirAccess(fss.vRefNum, fss.parID, nil, junk, junk, access) = noErr then begin
  702.                 locked := not btst(access, 26);
  703.             end; (* if *)
  704.         end; (* if *)
  705.         FileLocked := locked;
  706.     end; (* FileLocked *)
  707.  
  708.     function CopyFork (sourceForkRefnum, destForkRefnum: integer; bytesToCopy: longint): OSStatus;
  709.         (*    See comments in interface part. *)
  710.         const
  711.             kMaxCopyBufferSize = 65536;
  712.             kMinCopyBufferSize = 512;
  713.         var
  714.             err: OSStatus;
  715.             copyBuffer: Ptr;
  716.             copyBufferSize: longint;
  717.             numberOfBytesThisTime: longint;
  718.     begin
  719.         (* First attempt to allocate a copy buffer.  We do this by attempting
  720.             to allocate a buffer of size kMaxCopyBufferSize.  If that fails,
  721.             we divide the size by two and try again.  We keep trying until
  722.             the size drops below kMinCopyBufferSize, after which we
  723.             give up and return an error.
  724.         *)
  725.         err := noErr;
  726.         copyBufferSize := kMaxCopyBufferSize;
  727.         copyBuffer := nil;
  728.         repeat
  729.             copyBuffer := NewPtr(copyBufferSize);
  730.             if copyBuffer = nil then begin
  731.                 copyBufferSize := copyBufferSize div 2;
  732.             end; (* if *)
  733.         until (copyBuffer <> nil) or (copyBufferSize < kMinCopyBufferSize);
  734.         if copyBuffer = nil then begin
  735.             err := memFullErr;
  736.         end; (* if *)
  737.         
  738.         (* Now copy the file data, in copyBufferSize chunks. *)
  739.         while (err = noErr) & (bytesToCopy > 0) do begin
  740.             numberOfBytesThisTime := copyBufferSize;
  741.             if numberOfBytesThisTime > bytesToCopy then begin
  742.                 numberOfBytesThisTime := bytesToCopy;
  743.             end; (* if *)
  744.             err := FSRead(sourceForkRefnum, numberOfBytesThisTime, copyBuffer);
  745.             if err = noErr then begin
  746.                 bytesToCopy := bytesToCopy - numberOfBytesThisTime;
  747.                 err := FSWrite(destForkRefnum, numberOfBytesThisTime, copyBuffer);
  748.             end; (* if *)
  749.         end; (* while *)
  750.         
  751.         (* Clean up. *)
  752.         if copyBuffer <> nil then begin
  753.             DisposePtr(copyBuffer);
  754.         end; (* if *)
  755.         CopyFork := err;
  756.     end; (* CopyFork *)
  757.  
  758.     function CopyForkToFork (var sourceFile: FSSpec; var destFile: FSSpec;
  759.                             sourceRsrc: Boolean; destRsrc: Boolean): OSErr;
  760.         (*    See comments in interface part. *)
  761.         var
  762.             err: OSErr;
  763.             srcRefNum: integer;
  764.             destRefNum: integer;
  765.             sizeofSrcFork: longint;
  766.             junk: OSErr;
  767.     begin
  768.         (* Prepare for failure. *)
  769.         srcRefNum := 0;
  770.         destRefNum := 0;
  771.         
  772.         (* Open the source fork. *)
  773.         if sourceRsrc then begin
  774.             err := FSpOpenRF(sourceFile, fsRdPerm, srcRefNum);
  775.         end else begin
  776.             err := FSpOpenDF(sourceFile, fsRdPerm, srcRefNum);
  777.         end; (* if *)
  778.         if err <> noErr then begin
  779.             srcRefNum := 0;
  780.         end; (* if *)
  781.         
  782.         (* Open the dest fork. *)
  783.         if err = noErr then begin
  784.             if destRsrc then begin
  785.                 err := FSpOpenRF(destFile, fsRdWrPerm, destRefNum);
  786.             end else begin
  787.                 err := FSpOpenDF(destFile, fsRdWrPerm, destRefNum);
  788.             end; (* if *)
  789.             if err <> noErr then begin
  790.                 destRefNum := 0;
  791.             end; (* if *)
  792.         end; (* if *)
  793.  
  794.         (* Set the length of the dest fork to the length of the source fork. *)
  795.         if err = noErr then begin
  796.             err := GetEOF(srcRefNum, sizeofSrcFork);
  797.         end; (* if *)
  798.         if err = noErr then begin
  799.             err := SetEOF(destRefNum, sizeofSrcFork);
  800.         end; (* if *)
  801.  
  802.         (* Copy the fork. *)
  803.         if err = noErr then begin
  804.             err := CopyFork(srcRefNum, destRefNum, sizeofSrcFork);
  805.         end; (* if *)
  806.  
  807.         (* Clean up. *)
  808.         if srcRefNum <> 0 then begin
  809.             junk := FSClose(srcRefNum);
  810.         end; (* if *)
  811.         if destRefNum <> 0 then begin
  812.             junk := FSClose(destRefNum);
  813.         end; (* if *)
  814.  
  815.         junk := FlushVol(nil, destFile.vRefNum);
  816.  
  817.         CopyForkToFork := err;
  818.     end; (* CopyForkToFork *)
  819.     
  820.     function CopyFile (const source : FSSpec; const dest: FSSpec): OSStatus;
  821.         (* See comment in interface part. *)
  822.         var
  823.             err : OSStatus;
  824.             junk: OSStatus;
  825.             cpb: CInfoPBRec;
  826.             tmpSource : FSSpec;
  827.             tmpDest : FSSpec;
  828.     begin
  829.         tmpSource := source;
  830.         tmpDest := dest;
  831.         
  832.         (* Start off by deleting the destination file. *)
  833.         junk := FSpDelete(tmpDest);
  834.         
  835.         (* Copy both forks of the file. *)
  836.         err := FSpGetCatInfo(tmpSource, 0, cpb);
  837.         if err = noErr then begin
  838.             err := FSpCreate(tmpDest, cpb.ioFlFndrInfo.fdCreator, cpb.ioFlFndrInfo.fdType, smSystemScript);
  839.         end; (* if *)
  840.         if err = noErr then begin
  841.             err := CopyForkToFork (tmpSource, tmpDest, false, false);
  842.         end; (* if *)
  843.         if err = noErr then begin
  844.             err := CopyForkToFork (tmpSource, tmpDest, true, true);
  845.         end; (* if *)
  846.         
  847.         (* Set the catalogue info for the tmpDestination file. *)
  848.         if err = noErr then begin
  849.             err := FSpSetCatInfo(tmpDest, cpb);
  850.         end; (* if *)
  851.         
  852.         (* Clean up.  Delete the file if we didn't succeed completely. *)
  853.         if err <> noErr then begin
  854.             junk := FSpDelete(tmpDest);
  855.         end; (* if *)
  856.         CopyFile := err;
  857.     end; (* CopyFile *)
  858.  
  859.     function GetFSSpecGivenFileRefNum(fileRefNum : integer; var fss : FSSpec) : OSStatus;
  860.         (* Consults the FCB of the open file to find out its FSSpec. *)
  861.         var
  862.             err : OSStatus;
  863.             fcbPB : FCBPBRec;
  864.     begin
  865.         fcbPB.ioNamePtr := @fss.name;
  866.         fcbPB.ioRefNum := fileRefNum;
  867.         fcbPB.ioFCBIndx := 0;
  868.         fcbPB.ioVRefNum := 0;
  869.         err := PBGetFCBInfoSync(@fcbPB);
  870.         if err = noErr then begin
  871.             fss.vRefNum := fcbPB.ioFCBVRefNum;
  872.             fss.parID := fcbPB.ioFCBParID;
  873.         end; (* if *)
  874.         GetFSSpecGivenFileRefNum := err;
  875.     end; (* GetFSSpecGivenFileRefNum *)
  876.  
  877.     function FileRefNumIsWriteable(fileRefNum : integer; var writeable : Boolean) : OSStatus;
  878.         (* See comment in interface part. *)
  879.         var
  880.             err : OSStatus;
  881.             fcbPB : FCBPBRec;
  882.     begin
  883.         fcbPB.ioNamePtr := nil;
  884.         fcbPB.ioRefNum := fileRefNum;
  885.         fcbPB.ioFCBIndx := 0;
  886.         fcbPB.ioVRefNum := 0;
  887.         err := PBGetFCBInfoSync(@fcbPB);
  888.         if err = noErr then begin
  889.             writeable := band(fcbPB.ioFCBFlags, bsl(fcbWriteMask, 8)) <> 0;
  890.         end; (* if *)
  891.         FileRefNumIsWriteable := err;
  892.     end; (* FileRefNumIsWriteable *)
  893.  
  894.     function EqualFSSpec(const fss1 : FSSpec; const fss2 : FSSpec) : Boolean;
  895.         (* Returns true if fss1 and fss2 denote the same file system object. *)
  896.     begin
  897.         EqualFSSpec := (fss1.vRefNum = fss2.vRefNum) &
  898.                         (fss1.parID = fss2.parID) &
  899.                         EqualString(fss1.name, fss2.name, false, true);
  900.     end; (* EqualFSSpec *)
  901.  
  902.     function ICUFSSpecToFullPath (const fss: FSSpec; var path: Str255): OSErr;
  903.         (* See comment in interface part. *)
  904.         var
  905.             err: OSErr;
  906.             pb: CInfoPBRec;
  907.             tmpFSS : FSSpec;
  908.     begin
  909.         tmpFSS := fss;
  910.         
  911.         err := noErr;
  912.         if tmpFSS.parID = 1 then begin
  913.             (* It's a volume, just return the “name:”. *)
  914.             path := concat(tmpFSS.name, ':');
  915.         end else begin
  916.         
  917.             (* It's a file or folder, start by putting the name at the end of the path
  918.                 and then iterate up the directory hierarchy adding directory names to
  919.                 the front of the path.
  920.             *)
  921.             path := tmpFSS.name;
  922.             while (err = noErr) & (tmpFSS.parID <> 1) do begin
  923.                 err := FSpGetCatInfo(tmpFSS, -1, pb);
  924.                 path := concat(tmpFSS.name, ':', path);
  925.                 tmpFSS.parID := pb.ioFlParID;
  926.             end; (* while *)
  927.             
  928.         end; (* if *)
  929.         ICUFSSPecToFullPath := err;
  930.     end; (* ICUFSSPecToFullPath *)
  931.  
  932.     function FindVolumeByNameAndDate (name: Str31; creationDate: longint; var vRefNum: integer): OSErr;
  933.         (* Attempts to find a volume based on it's name and creation date.  This
  934.             is the tricky part of our "poor man's alias resolution" scheme.  An
  935.             ICFileSpec stores the volume name and creation date of the item
  936.             it points to.  These are used to try to find the matching volume
  937.             in systems that don't have the Alias Manager.  This routine implements
  938.             that finding code.
  939.             
  940.             The routine takes a two phase approach.  In the first phase, it searches for
  941.             volumes by name and creation date.  If it can't find a match, it proceeds
  942.             to the second phase where a matching name is considered good enough.
  943.             
  944.             Please don't blame me for the number of "leave"s in this code.  Peter
  945.             wrote it, and I've learnt through hard experience that I'm too stupid
  946.             to mess with his code too much.
  947.         *)
  948.         var
  949.             err: OSErr;
  950.             phase : (kMatchNameAndCreationDate, kMatchOnlyName);
  951.             volumeName: Str255;
  952.             volumeIndex: integer;
  953.             pb: HParamBlockRec;
  954.     begin
  955.         for phase := kMatchNameAndCreationDate to kMatchOnlyName do begin
  956.  
  957.             volumeIndex := 1;
  958.             while true do begin
  959.                 (* Get info an the volumeIndex'th volume. *)
  960.                 volumeName := '';
  961.                 pb.ioNamePtr := @volumeName;
  962.                 pb.ioVolIndex := volumeIndex;
  963.                 err := PBGetVInfoSync(@pb);
  964.                 if err <> noErr then begin
  965.                     leave;
  966.                 end; (* if *)
  967.  
  968.                 (* Check for a match. *)
  969.                 if EqualString(name, volumeName, false, true) then begin
  970.                     if (phase = kMatchOnlyName) or (pb.ioVCrDate = creationDate) then begin
  971.                         leave;
  972.                     end; (* if *)
  973.                 end; (* if *)
  974.  
  975.                 volumeIndex := volumeIndex + 1;
  976.             end; (* while *)
  977.  
  978.             (* Leave if we found a match. *)
  979.             if err = noErr then begin
  980.                 leave;
  981.             end; (* if *)
  982.         end; (* for *)
  983.         
  984.         (* Return the vRefNum of the found volume. *)
  985.         if err = noErr then begin
  986.             vRefNum := pb.ioVRefNum;
  987.         end; (* if *)
  988.         FindVolumeByNameAndDate := err;
  989.     end; (* FindVolumeByNameAndDate *)
  990.  
  991.     function ICFileSpecToFSSpec (fileSpec: ICFileSpecHandle; canInteract: Boolean; var fss: FSSpec): ICError;
  992.         (* See comment in interface part. *)
  993.         var
  994.             err: ICError;
  995.             junkLong: longint;
  996.             aliasH: AliasHandle;
  997.             aliasCount: integer;
  998.             aliasMatchRules: longint;
  999.             junkBool: Boolean;
  1000.             cpb: CInfoPBRec;
  1001.     begin
  1002.         err := noErr;
  1003.         if (err = noErr) & (GetHandleSize(Handle(fileSpec)) < sizeof(ICFileSpec)) then begin
  1004.             err := paramErr;
  1005.         end; (* if *)
  1006.         
  1007.         if err = noErr then begin
  1008.  
  1009.             (* Try to find it using the alias embedded in the ICFileSpec. *)
  1010.             err := -1;
  1011.             if (fileSpec^^.alias.aliasSize <> 0) then begin
  1012.                 (* Make a copy of the ICFileSpecHandle. *)
  1013.                 aliasH := AliasHandle(fileSpec);
  1014.                 err := HandToHand(Handle(aliasH));
  1015.                 if err = noErr then begin
  1016.                     (* Use Munger to delete our fields from the front of the copy, thereby
  1017.                         turning it into a real AliasHandle.
  1018.                     *)
  1019.                     junkLong := Munger(Handle(aliasH), 0, nil, sizeof(ICFileSpec) - sizeof(AliasRecord), @junkLong, 0);
  1020.  
  1021.                     (* Call the Alias Manager to find the match. *)
  1022.                     aliasCount := 1;
  1023.                     aliasMatchRules := kARMSearch + kARMMountVol;
  1024.                     if canInteract & (ICUCanInteract <> noErr) then begin
  1025.                         aliasMatchRules := aliasMatchRules + kARMNoUI;
  1026.                     end; (* if *)
  1027.                     err := MatchAlias(nil, aliasMatchRules, aliasH, aliasCount, @fss, junkBool, nil, nil);
  1028.  
  1029.                     (* Dispose our copy of the alias. *)
  1030.                     DisposeHandle(Handle(aliasH));
  1031.                 end; (* if *)
  1032.             end; (* if *)
  1033.  
  1034.             (* If it we didn't find it, try using our poor man's alias. *)
  1035.             if err <> noErr then begin
  1036.  
  1037.                 (* Attempt to find a matching volume. *)
  1038.                 err := FindVolumeByNameAndDate(fileSpec^^.vol_name, fileSpec^^.vol_creation_date, fss.vRefNum);
  1039.  
  1040.                 (* If it worked, build an FSSpec for the item and confirm it's existance using
  1041.                     GetCatInfo.
  1042.                 *)
  1043.                 if err = noErr then begin
  1044.                     fss.parID := fileSpec^^.fss.parID;
  1045.                     fss.name := fileSpec^^.fss.name;
  1046.                     err := FSpGetCatInfo(fss, 0, cpb);
  1047.                 end; (* if *)
  1048.             end; (* if *)
  1049.         end; (* if *)
  1050.  
  1051.         ICFileSpecToFSSpec := err;
  1052.     end; (* ICFileSpecToFSSpec *)
  1053.  
  1054.     function FSSpecToICFileSpec (var fss: FSSpec; fileSpec: ICFileSpecHandle): OSErr;
  1055.         (* See comment in interface part. *)
  1056.         var
  1057.             err: OSErr;
  1058.             pb: HParamBlockRec;
  1059.             volumeName: Str63;
  1060.             aliasH: AliasHandle;
  1061.             junkLong: longint;
  1062.     begin
  1063.         (* First resize the handle to the basic size and fill in our poor man's alias
  1064.             information.
  1065.         *)
  1066.         SetHandleSize(Handle(fileSpec), sizeof(ICFileSpec));
  1067.         err := MemError;
  1068.         if err = noErr then begin
  1069.             
  1070.             (* Get the volume information. *)
  1071.             volumeName := '';
  1072.             pb.ioNamePtr := @volumeName;
  1073.             pb.ioVRefNum := fss.vRefNum;
  1074.             pb.ioVolIndex := 0;
  1075.             err := PBGetVInfoSync(@pb);
  1076.             
  1077.             (* Fill in the basic fields of the ICFileSpec. *)
  1078.             if err = noErr then begin
  1079.                 fileSpec^^.vol_creation_date := pb.ioVCrDate;
  1080.                 fileSpec^^.vol_name := volumeName;
  1081.                 fileSpec^^.fss := fss;
  1082.                 fileSpec^^.alias.userType := OSType(0);
  1083.                 fileSpec^^.alias.aliasSize := 0;
  1084.             end; (* if *)
  1085.         end; (* if *)
  1086.  
  1087.         (* Now, if we have the Alias Manager, create an alias and append it to the handle. 
  1088.             This is entirely optional, so we make sure that any errors encountered in this
  1089.             process don't make it out to the client.
  1090.         *)
  1091.         if (err = noErr) then begin
  1092.         
  1093.             (* Create the alias. *)
  1094.             err := NewAlias(nil, fss, aliasH);
  1095.             if err = noErr then begin
  1096.                 
  1097.                 (* Append it to the end of the fileSpec and then delete the dummy
  1098.                     AliasRecord from the end of the original fileSpec.
  1099.                 *)
  1100.                 err := HandAndHand(Handle(aliasH), Handle(fileSpec));
  1101.                 if err = noErr then begin
  1102.                     junkLong := Munger(Handle(fileSpec), sizeof(ICFileSpec) - sizeof(AliasRecord), nil, sizeof(AliasRecord), @junkLong, 0);
  1103.                 end; (* if *)
  1104.                 DisposeHandle(Handle(aliasH));
  1105.             end; (* if *)
  1106.             err := noErr;
  1107.         end; (* if *)
  1108.  
  1109.         FSSpecToICFileSpec := err;
  1110.     end; (* FSSpecToICFileSpec *)
  1111.  
  1112.     function IsApplicationType(fdType : OSType) : Boolean;
  1113.         (* See comment in interface part. *)
  1114.     begin
  1115.         // If you add extra types, you should also make a similar change
  1116.         // in ICStandardGetFile in "ICStandardFile.p".
  1117.         
  1118.         IsApplicationType := (fdType = 'APPL') | (fdType = 'APPC') | (fdType = 'appe');
  1119.     end; (* IsApplicationType *)
  1120.  
  1121.     (* ***** IC API Stuff ***** *)
  1122.  
  1123. {$PUSH}
  1124. {$R-}
  1125.     function ICGetPrefStr (inst: ICInstance; key: Str255; var attr: ICAttr; var str: Str255): ICError;
  1126.         (* See comment in interface part. *)
  1127.         var
  1128.             err: ICError;
  1129.             size: longint;
  1130.     begin
  1131.         size := 256;
  1132.         err := ICGetPref(inst, key, attr, @str, size);
  1133.         if err <> noErr then begin
  1134.             str := '';
  1135.         end; (* if *)
  1136.         ICGetPrefStr := err;
  1137.     end; (* ICGetPrefStr *)
  1138.  
  1139.     function ICSetPrefStr (inst: ICInstance; key: Str255; attr: ICAttr; str: Str255): ICError;
  1140.         (* See comment in interface part. *)
  1141.     begin
  1142.         ICSetPrefStr := ICSetPref(inst, key, attr, @str, length(str) + 1);
  1143.     end; (* ICSetPrefStr *)
  1144. {$POP}
  1145.  
  1146.     (* ***** Text Utilities Stuff ***** *)
  1147.  
  1148.     function DecStr(aNumber: longint): Str255;
  1149.         (* See comment in interface part. *)
  1150.         var
  1151.             result : Str255;
  1152.     begin
  1153.         NumToString(aNumber, result);
  1154.         DecStr := result;
  1155.     end; (* DecStr *)
  1156.  
  1157.     function DecVal(aString : Str255) : longint;
  1158.         (* See comment in interface part. *)
  1159.         var
  1160.             result : longint;
  1161.     begin
  1162.         StringToNum(aString, result);
  1163.         DecVal := result;
  1164.     end; (* DecVal *)
  1165.  
  1166.     function StringToOSType (aString: Str255): OSType;
  1167.         (* See comment in interface part. *)
  1168.         var
  1169.             result: OSType;
  1170.     begin
  1171.         aString := concat(aString, chr(0), chr(0), chr(0), chr(0));
  1172.         BlockMoveData(@aString[1], @result, 4);
  1173.         StringToOSType := result;
  1174.     end; (* StringToOSType *)
  1175.  
  1176.     function OSTypeToString (anOSType: OSType): Str15;
  1177.         (* See comment in interface part. *)
  1178.         var
  1179.             result : Str15;
  1180.     begin
  1181.         result := concat(chr(0),chr(0),chr(0),chr(0));
  1182.         BlockMoveData(@anOSType, @result[1], 4);
  1183.         OSTypeToString := result;
  1184.     end; (* OSTypeToString *)
  1185.  
  1186.     function TPCopy (sourceString: string; startIndex : integer; count: integer): string;
  1187.         (* See comment in interface part. *)
  1188.     begin
  1189.         (* Check for startIndex being before the first character in the string. *)
  1190.         if startIndex < 1 then begin
  1191.             count := count - (1 - startIndex);
  1192.             startIndex := 1;
  1193.         end; (* if *)
  1194.         
  1195.         (* Check for a request for more characters than are in the string. *)
  1196.         if (startIndex + count) > length(sourceString) then begin
  1197.             count := length(sourceString) - startIndex + 1;
  1198.         end; (* if *)
  1199.         
  1200.         (* Trim count. *)
  1201.         if count < 0 then begin
  1202.             count := 0;
  1203.         end; (* if *)
  1204.         
  1205.         (* Extract the string data. *)
  1206.         sourceString[0] := chr(count);
  1207.         BlockMoveData(@sourceString[startIndex], @sourceString[1], count);
  1208.  
  1209.         TPCopy := sourceString;
  1210.     end; (* TPCopy *)
  1211.  
  1212.     function GetOwnerName : Str255;
  1213.         (* See comment in interface part. *)
  1214.         const
  1215.             rOwnerNameString = -16096;
  1216.         var
  1217.             strH: StringHandle;
  1218.     begin
  1219.         strH := GetString(rOwnerNameString);
  1220.         if strH <> nil then begin
  1221.             (* Don't release it, someone else might be using it. *)
  1222.             GetOwnerName := strH^^;
  1223.         end else begin
  1224.             GetOwnerName := '';
  1225.         end; (* if *)
  1226.     end; (* GetOwnerName *)
  1227.  
  1228.     function NewLookupError(NersID : integer; errNum : OSStatus) : Str255;
  1229.         (* See comment in interface part. *)
  1230.         var
  1231.             result : Str255;
  1232.             errH : Handle;
  1233.             s : SInt8;
  1234.             candidateErrNum : longint;
  1235.             errsDataPtr : BigBufferPtr;
  1236.             indexIntoErrsData : longint;
  1237.             maxIndexIntoErrsData : longint;
  1238.             found : Boolean;
  1239.     begin
  1240.         result := '';
  1241.         errH := GetResource('Ners', NersID);
  1242.         if errH <> nil then begin
  1243.             s := HGetState(errH);
  1244.             HLock(errH);
  1245.             errsDataPtr := BigBufferPtr(errH^);
  1246.             
  1247.             indexIntoErrsData := 0;
  1248.             maxIndexIntoErrsData := GetHandleSize(errH);
  1249.             found := false;
  1250.             (* Loop through the resource looking for a match. *)
  1251.             while (indexIntoErrsData < maxIndexIntoErrsData) and not found do begin
  1252.                 (* Extract the error number.
  1253.                      I use BlockMoveData here because the data may not be word aligned, and
  1254.                         original 68Ks will take an Address Error if I attempt to move an
  1255.                         unaligned longint.
  1256.                 *)
  1257.                 BlockMoveData(@errsDataPtr^[indexIntoErrsData], @candidateErrNum, sizeof(longint));
  1258.                 indexIntoErrsData := indexIntoErrsData + sizeof(longint);
  1259.  
  1260.                 (* Extract the error string. *)
  1261.                 BlockMoveData(@errsDataPtr^[indexIntoErrsData], @result, errsDataPtr^[indexIntoErrsData] + 1);
  1262.                 indexIntoErrsData := indexIntoErrsData + errsDataPtr^[indexIntoErrsData] + 1;
  1263.  
  1264.                 (* Figure out whether we've found what we're looking for. *)
  1265.                 found := (candidateErrNum = errNum) or (candidateErrNum = 0)
  1266.             end; (* while *)
  1267.             
  1268.             if not found then begin
  1269.                 result := '';
  1270.             end; (* if *)
  1271.             HSetState(errH, s);
  1272.         end; (* if *)
  1273.         NewLookupError := result;
  1274.     end; (* NewLookupError *)
  1275.  
  1276.     procedure NewLookupErrorC(NersID : integer; errNum : OSStatus; var result : Str255);
  1277.         (* See comment in interface part. *)
  1278.     begin
  1279.         result := NewLookupError(NersID, errNum);
  1280.     end; (* NewLookupErrorC *)
  1281.  
  1282.     (* ***** Truly Misc Stuff ***** *)
  1283.  
  1284.     function NumToolboxTraps: integer;
  1285.         (* Returns the number of toolbox traps on this machine. *)
  1286.     begin
  1287.         if NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap) then begin
  1288.             NumToolboxTraps := $200
  1289.         end else begin
  1290.             NumToolboxTraps := $400;
  1291.         end; (* if *)
  1292.     end; (* NumToolboxTraps *)
  1293.  
  1294.     function GetTrapType (theTrap: integer): TrapType;
  1295.         (* Returns the trap type associated with the given A-Trap number. *)
  1296.         const
  1297.             TrapMask = $0800;
  1298.     begin
  1299.         if band(theTrap, TrapMask) > 0 then begin
  1300.             GetTrapType := ToolTrap
  1301.         end else begin
  1302.             GetTrapType := OSTrap;
  1303.         end; (* if *)
  1304.     end; (* GetTrapType *)
  1305.  
  1306.     function TrapAvailable (theTrap: integer): Boolean;
  1307.         (* See comment in interface part. *)
  1308.         var
  1309.             tType: TrapType;
  1310.     begin
  1311.         tType := GetTrapType(theTrap);
  1312.         if tType = ToolTrap then begin
  1313.             theTrap := band(theTrap, $07FF);
  1314.             if theTrap >= NumToolboxTraps then begin
  1315.                 theTrap := _Unimplemented;
  1316.             end; (* if *)
  1317.         end; (* if *)
  1318.         TrapAvailable := NGetTrapAddress(theTrap, tType) <> NGetTrapAddress(_Unimplemented, ToolTrap);
  1319.     end; (* TrapAvailable *)
  1320.  
  1321.     {$ifc not GENERATINGCFM}
  1322.     
  1323.     (*    Some utter gumby forgot that you might want to generate
  1324.         68K code from a PPC binary, and so FlushCodeCacheRange is
  1325.         not exported to CFM clients.  You can write your own glue,
  1326.         but in the case of IC it's just simpler to comment out the code
  1327.         for PPC builds because I don't need this functionality from
  1328.         my PPC code.
  1329.     *)
  1330.     
  1331.     (*    Some utter gumby forgot that FlushCodeCacheRange is supposed
  1332.         to return an error code, and defined it wrong in the Universal
  1333.         Interfaces.  This error is *finally* fixed in version 3.x
  1334.         of the interfaces, but we're using 2.x at the moment, so we
  1335.         still have to define our own.
  1336.  
  1337.         By the way, we define this EXTERNAL if we're building CFM,
  1338.         so that the compiler doesn't complain about the fact we
  1339.         don't implement the CFM side of it (which is kinda tricky).
  1340.         That puts off the error until link time.  If I find that
  1341.         I actually need to call this from CFM code, I guess 
  1342.         I'll have to knuckle down and write the glue.
  1343.     *)
  1344.     
  1345.     FUNCTION QFlushCodeCacheRange(address: UNIV Ptr; count: LONGINT) : OSErr;
  1346.         {$IFC NOT GENERATINGCFM}
  1347.         INLINE $225F, $205F, $7009, $A098, $3E80;
  1348.         {$ELSEC}
  1349.         EXTERNAL;
  1350.         {$ENDC}
  1351.     
  1352.     procedure MakeDataExecutableAs68KCode(base : Ptr; size : longint);
  1353.     begin
  1354.         if TrapAvailable(_HWPriv) then begin
  1355.             if QFlushCodeCacheRange(base, size) <> noErr then begin
  1356.                 FlushCodeCache;
  1357.             end; (* if *)
  1358.         end; (* if *)
  1359.     end; (* MakeDataExecutableAs68KCode *)
  1360.     
  1361.     {$endc}
  1362.  
  1363.     function ICUCanInteract: ICError;
  1364.         (* See comment in interface part. *)
  1365.         var
  1366.             err: ICError;
  1367.             gestaltResponse : longint;
  1368.     begin
  1369.         err := noErr;
  1370.         if (Gestalt(gestaltAppleEventsAttr, gestaltResponse) = noErr) &
  1371.                                     btst(gestaltResponse, gestaltAppleEventsPresent) then begin
  1372.             err := AEInteractWithUser(kAEDefaultTimeout, nil, nil);
  1373.         end; (* if *)
  1374.         ICUCanInteract := err;
  1375.     end; (* ICUCanInteract *)
  1376.  
  1377.         
  1378.     procedure SafeAppendMenu (menuH: MenuHandle; itemText: Str255);
  1379.         (* See comment in interface part. *)
  1380.     begin
  1381.         AppendMenu(menuH, 'fred');
  1382.  
  1383.         // If the string begins with a '-', we must change it
  1384.         // before calling SetMenuItemText because the system inteprets
  1385.         // a leading '-' as a disabled item, even in SetMenuItemText.
  1386.         // This code is WorldScript safe because the first byte of
  1387.         // the string is either a) the first byte of a two byte 
  1388.         // character, in which case it must be high bit set character,
  1389.         // and '-' isn't, b) a single byte character, in which case
  1390.         // the comparison makes sense because all script systems
  1391.         // contain Roman as the first 128 values of the one byte
  1392.         // characters.
  1393.  
  1394.         if (length(itemText) > 0) & (itemText[1] = '-') then begin
  1395.             itemText[1] := chr(0);
  1396.         end; (* if *)
  1397.  
  1398.         SetMenuItemText(menuH, CountMItems(menuH), itemText);
  1399.     end; (* SafeAppendMenu *)
  1400.  
  1401. end. (* ICCommonSubs *)
  1402.